﻿namespace EFS.RC.CC.Tools.Functions.Operator
{
	/// <summary>
	/// Transfer
	/// </summary>
	internal class Trasfer : EFS.Common.Async.AsyncRoot
	{
		#region Composition

		// sync access
		private object syncAccess = new object();

		/// From DDI
        private EFS.RC.CC.Sessions.OfficeService.Operators.Operator fromOperatorOfficeServiceSession;

		/// To DDI
        private EFS.RC.CC.Sessions.OfficeService.Operators.DialIn toDialInOperatorOfficeServiceSession;

		/// <summary>
		/// Status
		/// </summary>
		internal enum TransferStatus { Idle, Started, Terminated }

		// Status
		private TransferStatus status = TransferStatus.Idle;

		#endregion

		#region Constructor
		
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="fromOperatorOfficeServiceSession"></param>
		/// <param name="transferDestination"></param>
		internal Trasfer(EFS.RC.CC.Sessions.OfficeService.Operators.Operator fromOperatorOfficeServiceSession, System.String transferDestination)
		{
			// log
            EFS.EventLog.Log.LogInfo("EFS.RC.CC.Tools.Functions.DirectDial:ctor");

			// check
			EFS.Common.Verify.Reference(fromOperatorOfficeServiceSession, "fromOperatorOfficeServiceSession");

            // keep
            this.fromOperatorOfficeServiceSession = fromOperatorOfficeServiceSession;

            // make a presence with participant destination
			EFS.RC.CC.Presences.Operators.Presence presence = new EFS.RC.CC.Presences.Operators.Anonymous(transferDestination);

            // make ddi
            toDialInOperatorOfficeServiceSession = new EFS.RC.CC.Sessions.OfficeService.Operators.DialIn(presence, 
                fromOperatorOfficeServiceSession.CustomerSession);
            
            // register for establish
			toDialInOperatorOfficeServiceSession.AsinkEstablishedDelegate.Register(OnOfficeServiceConferenceParticipantEstablished);
			toDialInOperatorOfficeServiceSession.AsinkTerminatedDelegate.Register(OnOfficeServiceConferenceParticipantTerminated);
        }

		#endregion

		#region Usage

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Tools.Functions.DirectDial.Transfer [{0}, {1}]", fromOperatorOfficeServiceSession.Describe(), toDialInOperatorOfficeServiceSession.Describe());
		}

    	/// <summary>
		/// Begin startup
		/// </summary>
		internal EFS.Common.Async.AsyncProcess BeginTransfer()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start
            return BeginAsyncProcess2(ExecuteTransfer, OnExecuteTransferComplete);
		}

		// Status
		internal TransferStatus Status
		{
			get { return status; }
		}
		#endregion

		#region Transfer

		/// <summary>
		/// Startup
		/// </summary>
        private void ExecuteTransfer()
		{
			// log
			EFS.EventLog.Log.LogInfo("ExecuteTransfer", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (status != TransferStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle Transfer can be started");
				}

				// started
				status = TransferStatus.Started;
			}

			// SAL
            EFS.RC.CC.Tools.Loggers.SAL.Add(fromOperatorOfficeServiceSession.CustomerSession.ConversationGuid, "ExecuteTransfer", this);

            // stat ddi
            toDialInOperatorOfficeServiceSession.BeginStartup();
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
        private void OnExecuteTransferComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				HandleExceptionSafe(ex);
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			EFS.EventLog.Log.LogInfo("OnException", this);

			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// SAL
            EFS.RC.CC.Tools.Loggers.SAL.Add(fromOperatorOfficeServiceSession.CustomerSession.ConversationGuid, "OnException", ex, this);
		}

		/// <summary>
		/// Customer session terminated
		/// </summary>
		/// <param name="playerDistribution"></param>
		private void OnOfficeServiceConferenceParticipantTerminated(EFS.RC.CC.Sessions.OfficeService.IParticipant participantSession)
		{
			EFS.EventLog.Log.LogInfo("OnOfficeServiceConferenceTerminated", this);

			// check
			EFS.Common.Verify.Reference(participantSession, "participantSession");

			// check mine
			EFS.Common.Verify.Condition(participantSession.Equals(toDialInOperatorOfficeServiceSession), "Established OfficeService Conferece Participant is different");

			// lock access to status
			lock (syncAccess)
			{
				// check started
				if (status == TransferStatus.Started)
				{
					// done
					status = TransferStatus.Terminated;
				}
			}
		}

		/// <summary>
		/// Customer session terminated
		/// </summary>
		/// <param name="playerDistribution"></param>
        private void OnOfficeServiceConferenceParticipantEstablished(EFS.RC.CC.Sessions.OfficeService.IParticipant participantSession)
		{
			EFS.EventLog.Log.LogInfo("OnOfficeServiceConferenceTerminated", this);

			// check
			EFS.Common.Verify.Reference(participantSession, "participantSession");

            // check mine
            EFS.Common.Verify.Condition(participantSession.Equals(toDialInOperatorOfficeServiceSession), "Established OfficeService Conferece Participant is different");

			// lock access to status
			lock (syncAccess)
			{
				// check started
				if (status == TransferStatus.Started)
				{
					// close from
					fromOperatorOfficeServiceSession.BeginShutdown();

					// done
					status = TransferStatus.Terminated;
				}
			}
		}

		#endregion
	}
}
